﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Grundy.Common;
using Grundy.Entity;
using Grundy.Message;

namespace Grundy.Licensing.Server
{
    public abstract class LicensingStrategy : ILicensingStrategy
    {
        private readonly LicenseEntitlementValidity _licenseEntitlementValidity;
        public License License { get; private set; }
        public FeatureIdentificationKey FeatureIdentificationKey { get; private set; }
        protected ConcurrentDictionary<Guid, ClientSession> clientSessions = new ConcurrentDictionary<Guid, ClientSession>();

        protected LicensingStrategy(License license, LicenseEntitlementValidity licenseEntitlementValidity, FeatureIdentificationKey featureIdentificationKey)
        {
            _licenseEntitlementValidity = licenseEntitlementValidity;
            License = license;
            FeatureIdentificationKey = featureIdentificationKey;
        }

        public void Cleanup()
        {
            ClientSession tmp;
            clientSessions.Where(i => i.Value.HasExpired()).Select(i => i.Key).ToList()
            .ForEach(i => clientSessions.TryRemove(i, out tmp));
        }

        public IEnumerable<ClientSession> GetClientSessions()
        {
            return clientSessions.Values;
        }

        public bool HasClientSession(Guid sessionId)
        {
            return clientSessions.ContainsKey(sessionId);
        }

        public virtual long GetExhaustedLicenseCount()
        {
            return clientSessions.Count;
        }

        public virtual Guid Checkout(LicenseRequestInformation lri, object syncLock)
        {
            if (!_licenseEntitlementValidity.IsValid())
            {
                throw new LicenseNotBegunOrExpiredException(_licenseEntitlementValidity);
            }
            return CheckoutLicense(lri, syncLock);
        }

        protected abstract Guid CheckoutLicense(LicenseRequestInformation lri, object syncLock);

        public virtual bool Checkin(LicenseRequestInformation lri, object syncLock)
        {
            ClientSession cs;
            if (!clientSessions.TryRemove(lri.ClientSessionId, out cs))
            {
                throw new ClientSessionNotFoundException(lri.ClientSessionId, lri.EndPoint, FeatureIdentificationKey);
            }
            return true;
        }

        public virtual bool Reassert(Guid clientSessionId, EndPoint endpoint)
        {
            ClientSession cs;
            if (clientSessions.TryGetValue(clientSessionId, out cs))
            {
                cs.Reassert();
                return true;
            }
            return false;
        }

        protected Guid AddClientSession(LicenseRequestInformation lri, IdentifierCollection clientIdentifiersUsedToIdentify)
        {
            Guid clientSessionId = Guid.NewGuid();
            var clientSession = new ClientSession
            {
                EndPoint = lri.EndPoint,
                FeatureIdentificationKey = lri.FeatureIdentificationKey,
                Id = clientSessionId,
                Identifiers = lri.ClientIdentifiers,
                IdentifiersUsedToIdentify = clientIdentifiersUsedToIdentify
            };
            if (!clientSessions.TryAdd(clientSessionId, clientSession))
                throw new UnexpectedApplicationException("Unable to add client session to ClientSessions collection.");
            return clientSessionId;
        }

    }
}